/**************************************************************************/
/*! 
    @file     ds1307.c
    @author   K. Townsend (microBuilder.eu)
	
    @brief    Drivers for the DS1307 RTC

    @section DESCRIPTION

    The DS1307 is an I2C RTC with 128 bytes battery-backed up SRAM.

    @section LICENSE

    Software License Agreement (BSD License)

    Copyright (c) 2010, microBuilder SARL
    All rights reserved.

    Redistribution and use in source and binary forms, with or without
    modification, are permitted provided that the following conditions are met:
    1. Redistributions of source code must retain the above copyright
    notice, this list of conditions and the following disclaimer.
    2. Redistributions in binary form must reproduce the above copyright
    notice, this list of conditions and the following disclaimer in the
    documentation and/or other materials provided with the distribution.
    3. Neither the name of the copyright holders nor the
    names of its contributors may be used to endorse or promote products
    derived from this software without specific prior written permission.

    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY
    EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
    WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
    DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
    DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
    (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
    LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
    ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
    (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
    SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/**************************************************************************/
#include "ds1307.h"
#include "../core/systick.h"

extern volatile uint8_t   I2CMasterBuffer[I2C_BUFSIZE];
extern volatile uint8_t   I2CSlaveBuffer[I2C_BUFSIZE];
extern volatile uint32_t  I2CReadLength, I2CWriteLength;

static bool _ds1307Initialised = false;

/**************************************************************************/
/*! 
    @brief  Standard decimal to binary coded decimal
*/
/**************************************************************************/
uint8_t ds1307DecToBCD(uint8_t val)
{
  return ( (val/10*16) + (val%10) );
}

/**************************************************************************/
/*! 
    @brief  Binary coded decimal to standard decimal
*/
/**************************************************************************/
uint8_t ds1307BCDToDec(uint8_t val)
{
  return ( (val/16*10) + (val%16) );
}
   
/**************************************************************************/
/*! 
    @brief  Writes an 8 bit value
*/
/**************************************************************************/
ds1307Error_t ds1307Write8 (uint8_t address, uint8_t reg, uint32_t value)
{
  // Clear write buffers
  uint32_t i;
  for ( i = 0; i < I2C_BUFSIZE; i++ )
  {
    I2CMasterBuffer[i] = 0x00;
  }

  I2CWriteLength = 3;
  I2CReadLength = 0;
  I2CMasterBuffer[0] = address;
  I2CMasterBuffer[1] = reg;                       // Command register
  I2CMasterBuffer[2] = (value & 0xFF);            // Value to write
  i2cEngine();
  return DS1307_ERROR_OK;
}

/**************************************************************************/
/*! 
    @brief  Reads x bytes into a buffer
*/
/**************************************************************************/
ds1307Error_t ds1307ReadBuffer(uint8_t address, uint8_t reg, uint8_t *buffer, uint32_t len)
{
  if (len > I2C_BUFSIZE)
    return DS1307_ERROR_I2C_BUFFEROVERFLOW;

  // Clear write buffers
  uint32_t i;
  for ( i = 0; i < I2C_BUFSIZE; i++ )
  {
    I2CMasterBuffer[i] = 0x00;
  }

  I2CWriteLength = 2;
  I2CReadLength = len;
  I2CMasterBuffer[0] = address;
  I2CMasterBuffer[1] = reg;                       // Command register
  // Append address w/read bit
  I2CMasterBuffer[2] = address | DS1307_READBIT;
  i2cEngine();

  // Push response into buffer
  for ( i = 0; i < len; i++ )
  {
    buffer[i] = I2CSlaveBuffer[i];
  }

  return DS1307_ERROR_OK;
}

/**************************************************************************/
/*! 
    @brief  Initialises the I2C block
*/
/**************************************************************************/
ds1307Error_t ds1307Init(void)
{
  ds1307Error_t error = DS1307_ERROR_OK;
  uint8_t buffer[1];

  // Initialise I2C
  if (i2cInit(I2CMASTER) == false)
  {
    return DS1307_ERROR_I2C_INIT;    /* Fatal error */
  }
  
  // Make sure write is enabled on the ISL12202M (factory default = disabled)
//  error = ds1307ReadBuffer(DS1307_RTC_ADDRESS, DS1307_REG_CSR_INT, buffer, sizeof(buffer));
  error = ds1307ReadBuffer(DS1307_RTC_ADDRESS, DS1307_REG_RTC_SC, buffer, sizeof(buffer));
  if (!error)
  {
    if ((buffer[0] & 0x80))
    {
      // Write is not enabled on the RTC ... enable it now
      error = ds1307Write8(DS1307_RTC_ADDRESS, DS1307_REG_RTC_SC, buffer[0] & !DS1307_CLOCK_HALT);
    }
  // set OUTPIN to 1s blink
   error = ds1307Write8(DS1307_RTC_ADDRESS, DS1307_REG_CSR_CR, DS1307_OUT | DS1307_SQWE | !DS1307_RS1 | !DS1307_RS1);

    _ds1307Initialised = true;
  }

  return error;
}

/**************************************************************************/
/*! 
    @brief  Gets the current date/time from the RTC

    @section EXAMPLE

    @code
    #include "drivers/rtc/ds1307.h"
    ...
    ds1307Init();

    // Set the time to 12 June 2011 @ 11:59:30
    ds1307SetTime(0, 11, 6, 12, 11, 59, 30);

    // Wait 5 seconds
    systickDelay(5000);

    // Display the current time
    ds1307Time_t time;
    ds1307GetTime(&time);
    printf("DW:%d, Y:%d, M:%d, D:%d, H:%d, M:%d, S:%d\r\n", 
            time.dayofweek, 
            time.year, 
            time.month, 
            time.day, 
            time.hour, 
            time.minute, 
            time.second);
    @endcode
*/
/**************************************************************************/
ds1307Error_t ds1307GetTime(ds1307Time_t *time)
{
  ds1307Error_t error = DS1307_ERROR_OK;
  uint8_t buffer[7];

  if (!_ds1307Initialised)
  {
    error = ds1307Init();
    if (error) return error;
  }

  // Read 7 bytes at once into buffer
  error = ds1307ReadBuffer(DS1307_RTC_ADDRESS,
                              DS1307_REG_RTC_SC,
                              buffer, sizeof(buffer));

  if (!error)
  {
    time->second = ds1307BCDToDec(buffer[0]);
    time->minute = ds1307BCDToDec(buffer[1]);
    time->hour = buffer[2] & 0x1F; // 0x3F;
    time->dayofweek = buffer[3] & 0x07;
    time->day = buffer[4] & 0x3F;
    time->month = buffer[5] & 0x1F;
    time->year = buffer[6];
  }

  return error;
}

/**************************************************************************/
/*! 
    @brief  Sets the current date/time from the RTC

    @section EXAMPLE

    @code
    #include "drivers/rtc/ds1307.h"
    ...
    ds1307Init();

    // Set the time to 12 June 2011 @ 11:59:30
    ds1307SetTime(0, 11, 6, 12, 11, 59, 30);

    // Wait 5 seconds
    systickDelay(5000);

    // Display the current time
    ds1307Time_t time;
    ds1307GetTime(&time);
    printf("DW:%d, Y:%d, M:%d, D:%d, H:%d, M:%d, S:%d\r\n", 
            time.dayofweek, 
            time.year, 
            time.month, 
            time.day, 
            time.hour, 
            time.minute, 
            time.second);
    @endcode
*/
/**************************************************************************/
ds1307Error_t ds1307SetTime(uint8_t year, uint8_t month, uint8_t day, uint8_t dayofweek, uint8_t hour, uint8_t minute, uint8_t second)
{
  ds1307Error_t error = DS1307_ERROR_OK;

  if (!_ds1307Initialised)
  {
    error = ds1307Init();
    if (error) return error;
  }

  error = ds1307Write8(DS1307_RTC_ADDRESS, DS1307_REG_RTC_SC, ds1307DecToBCD(second));
  if (error) return error;
  error = ds1307Write8(DS1307_RTC_ADDRESS, DS1307_REG_RTC_MN, ds1307DecToBCD(minute));
  if (error) return error;
  // Always append military flag to hour (24 hour only)
  error = ds1307Write8(DS1307_RTC_ADDRESS, DS1307_REG_RTC_HR, (hour & ( 0x3F | !DS1307_HR_MILITARY)));
  if (error) return error;
  error = ds1307Write8(DS1307_RTC_ADDRESS, DS1307_REG_RTC_DW, dayofweek & 0x07);
  if (error) return error;
  error = ds1307Write8(DS1307_RTC_ADDRESS, DS1307_REG_RTC_DT, day & 0x3F);
  if (error) return error;
  error = ds1307Write8(DS1307_RTC_ADDRESS, DS1307_REG_RTC_MO, month & 0x1F);
  if (error) return error;
  error = ds1307Write8(DS1307_RTC_ADDRESS, DS1307_REG_RTC_YR, year);
  if (error) return error;

  return error;
}
